/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#ifndef __SG_SCENENODE_H__
#define __SG_SCENENODE_H__

#pragma once
#include <list>
#include <vector>

#include "model/BoundingSphere.h"
#include "model/AABoundingBox.h"
#include "ITransformable.h"
#include "Frustum.h"
#include "Thread.h"
class IRenderer;
class IScene;

#define POSITION_LAST -1

namespace sg {

/** 
 *  A very simple scene graph node
 */
class DLL_EXPORT SceneNode
	: public IResource
{
public:
	/// Constructs this node with without any parents
	SceneNode(void);

	/// Constructs this node as child of a parent
	SceneNode(SceneNode* parent);

	/// Updates all children of this node
	virtual void updateNodes(double dt);

	/// Draws all children of this node
	virtual void drawNodes(bool drawTransparent = false, math::Matrix4 *postMat = 0);

	/// Adds a node as a child to this node
	void addChild(SceneNode* child, bool isTransparent = false, int position = POSITION_LAST);
	
	/// Removes a child node from this node. If del is true, the object will be deleted
	void removeChild(SceneNode* child, bool del = true);

	/// Deletes all children from this node
	void clear();

	/// Finds a SceneNode that is a child of this node and return it. Returns NULL if no occurence is found
	SceneNode* find(SceneNode* child);

	/// Get the current Transformation
	ITransformable* getTransform();

	/// Sets a new transformation for the scene node. This affects all children.
	void setTransform( ITransformable *trans);

	/// Returns this matrix in world coordinates
	math::Matrix4 getWorldTransformMatrix(math::Matrix4 *currentMat = NULL) const;

	/// Draws the current scene node. Should be overwritten by inheriting classes.
	virtual void draw() {};
	virtual void drawBoundingSphere();
	virtual void update(double dt) {};

	typedef std::list<SceneNode*> child_list;

	/// Returns reference to a list of all children of the scene node
	child_list* getChildren() { return &lChildren; };

	/// Returns the bounding sphere of the scene node. Returns NULL if no bounding sphere was found.
	model::BoundingSphere* getBoundingSphere() { return &m_bbSphere; };

	/// Returns the axis aligned bounding box of the scene node. Returns NULL if no axis aligned bounding box was found.
	/// \todo Implement correct calulation of the bounding box
	model::AABoundingBox*  getBoundingBox()    { return &m_bbBox; };

	/// Calculate the actual bounding sphere
	virtual void calculateBoundingSphere() {};

	/// Called by any child after calculating or changing it's bounding sphere to let the parent know, that it needs to update it's own sphere
	void updateBoundingSphere();

	/// Updates the visibility of the scene node. Should be called for the root node of the scene graph.
	virtual void updateVisiblity(Frustum *frustum);

	/// Indicates if a scene node and all of it's children are visible
	const bool isVisible() const;

	/// Returns the number of children
	const size_t getNumberOfChildren() const;

	/// Returns a specific child from the list of children
	SceneNode* getChild( int x);

	/// Returns the class type as a string
	virtual std::string getClassType() const { return "sgSceneNode"; }

	/// Creates a copy of a specific subgraph and returns a reference to it
	virtual SceneNode* copy();

	bool selectObject(const math::Vector3 &rayOrigin, const math::Vector3 &rayDir, SceneNode **retObj, math::Vector3 *intersectionPoint);

	bool raySphereIntersection(SceneNode *node, const math::Vector3 &rayOrigin, const math::Vector3 &rayDir, math::Vector3 &intersection);

	/// Subdivide the children of this scene node into groups of children regarding to it's space. 
	/// The number of segments are the the number of partitions per side in a square
	virtual bool partition(int segX, int segY, int segZ);

	virtual void reset() {};
	virtual void release() {};

	SceneNode* getParent();

	/// Set the visibility to the specified value and lock this state, so it will not be set by updateVisibility
	void lockVisibility(bool visibilityValue);
	
	/// Unlock the visibility state
	void unlockVisibility();

	float distance;

	static const unsigned int GetVisibleNodes();

	static void SetCameraPosition(const math::Vector3 &campos);

	virtual unsigned int getType() { return RESTYPE_SCENENODE; };

	friend class ::IScene;

protected:
	virtual ~SceneNode(void);
	/// Sets a specified node as the parent of this node
	void setParent(SceneNode* parent);

	bool                   m_bIsRootNode;
	/// Please implement in derived classes
	virtual SceneNode* rawcopy() { return new SceneNode(); };

	SceneNode             *pParent;				///< A pointer to the parent element
	child_list              lTranspChildren;	///< The list of all the transparent children of the node
	child_list              lChildren;			///< The list of all opaque children of the node 
	model::BoundingSphere  m_bbSphere;			///< The bounding sphere of the node
	model::AABoundingBox   m_bbBox;				///< The bounding box of the node
	ITransformable        *m_pTransform;
	static IRenderer      *s_pRenderer;
	bool                   m_bVisible;
	int                    m_iRefCount;
	bool                   m_bDelete;
	bool                   m_bLockVisibility;

	static unsigned int s_uiVisibleObjects;

	static math::Vector3  s_vCamPos;

	thread::Mutex mutex;
};

/// Compare operator used for comparing the distance of two light sources. Uses the dist value
bool operator < (const SceneNode& l1, const SceneNode &l2);

} // namespace

#endif
